﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace OmniKits.Core
{
    public abstract class ThreadSafeJob : IRunJob
    {
        internal class JobState
        {
            public ManualResetEvent FinishEvent { get; } = new ManualResetEvent(false);
            public Thread JobThread { get; set; }
        }

        internal volatile JobState _State = null;

        protected abstract void Main();
        protected virtual bool Run(bool wait)
        {
            var old = _State;
            if (old != null)
            {
                if (wait)
                    old.FinishEvent.WaitOne();
                return false;
            }

            var state = new JobState();
            old = Interlocked.CompareExchange(ref _State, state, null);
            if (old != null)
            {
                if (wait)
                    old.FinishEvent.WaitOne();
                return false;
            }

            var evt = state.FinishEvent;
            try
            {
                var thread = new Thread(() =>
                {
                    try
                    {
                        Main();
                        evt.Set();
                    }
                    finally
                    {
                        Interlocked.CompareExchange(ref _State, null, old);
                        evt.Close();
                    }
                });
                state.JobThread = thread;
                thread.Start();

                if (wait)
                {
                    try
                    {
                        evt.WaitOne();
                    }
                    catch (ObjectDisposedException)
                    { }
                }

                return true;
            }
            catch
            {
                Interlocked.CompareExchange(ref _State, null, old);
                evt.Close();
                throw;
            }
        }
        public bool Start()
            => Run(false);
        public bool Run()
            => Run(true);

        public virtual bool Wait()
        {
            var state = _State;
            if (state == null)
                return false;

            try
            {
                state.FinishEvent.WaitOne();
                return true;
            }
            catch
            {
                return false;
            }
        }
    }

    public abstract class ThreadSafeJobCanStop : ThreadSafeJob, ICanStop
    {
        public virtual bool Stop()
        {
            var state = _State;
            if (state == null)
                return false;
            state = Interlocked.Exchange(ref _State, null);
            if (state == null)
                return false;

            var evt = state.FinishEvent;
            try {
                state.JobThread.Abort();
                evt.Set();
                evt.Close();
                return true;
            }
            catch
            {
                Interlocked.CompareExchange(ref _State, state, null);
                throw;
            }
        }
    }
}
